/**
 * <p>文件名:		XXX.java</p>
 * <p>版权:		CopyrightTag</p>
 * <p>公司:		CompanyTag</p>
 * @author		周华彬(zhouhuabin@chinatransinfo.com, zhou_hua_bin@163.com)
 */

package com.caits.lbs.framework.dao.impl;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.ibatis.binding.BindingException;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.binding.MapperProxy;
import org.apache.ibatis.session.SqlSession;

/**
 * <p>
 * LocalMapperProxy
 * </p>
 * <p>
 * 自行实现的本地代理mybatis映射类
 * </p>
 * @author 周华彬(zhouhuabin@chinatransinfo.com, zhou_hua_bin@163.com)
 * @version 0.0.0
 *          <table style="border:1px solid gray;">
 *          <tr>
 *          <th width="100px">版本号</th>
 *          <th width="100px">动作</th>
 *          <th width="100px">修改人</th>
 *          <th width="100px">修改时间</th>
 *          </tr>
 *          <!-- 以 Table 方式书写修改历史 -->
 *          <tr>
 *          <td>0.0.0</td>
 *          <td>创建类</td>
 *          <td>zhouhuabin</td>
 *          <td>2015年10月22日 上午10:43:33</td>
 *          </tr>
 *          <tr>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          </tr>
 *          </table>
 */
public class LocalMapperProxy implements InvocationHandler, Serializable {

	/** 变量:TODO,类型:long */
	private static final long serialVersionUID = 8230472886741549444L;
	private SqlSession sqlSession;

	private <T> LocalMapperProxy(SqlSession sqlSession) {
		this.sqlSession = sqlSession;
	}

	/**
	 * 创建代理类的实例 
	 * @param mapperInterface
	 * @param sqlSession
	 * @return T
	 */
	public static <T> T newMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
		ClassLoader classLoader = mapperInterface.getClassLoader();
		Class<?>[] interfaces = new Class[] { mapperInterface };
		LocalMapperProxy proxy = new LocalMapperProxy(sqlSession);
		MapperProxy xx;
		return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
	}

	/* (non-Javadoc)
	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
	 */
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (method.getDeclaringClass() == Object.class) {
			return method.invoke(this, args);
		}
		final Class<?> declaringInterface = findDeclaringInterface(proxy, method);
		final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
		final Object result = mapperMethod.execute(args);
		if (result == null && method.getReturnType().isPrimitive() && !method.getReturnType().equals(Void.TYPE)) {
			throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass()
					+ ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
		}
		return result;
	}

	private Class<?> findDeclaringInterface(Object proxy, Method method) {
		Class<?> declaringInterface = null;
		for (Class<?> iface : proxy.getClass().getInterfaces()) {
			try {
				Method m = iface.getMethod(method.getName(), method.getParameterTypes());
				if (declaringInterface != null) {
					throw new BindingException("Ambiguous method mapping.  Two mapper interfaces contain the identical method signature for " + method);
				} else if (m != null) {
					declaringInterface = iface;
				}
			} catch (Exception e) {
				// Intentionally ignore.
				// This is using exceptions for flow control,
				// but it's definitely faster.
			}
		}
		if (declaringInterface == null) {
			throw new BindingException("Could not find interface with the given method " + method);
		}
		return declaringInterface;
	}
}
